package com.sh.data.engine.domain.api.job;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.sh.data.engine.common.enumDefinition.DataApiCallStatusEnum;
import com.sh.data.engine.common.util.DateUtil;
import com.sh.data.engine.common.util.DateUtil.DATE_PATTERN;
import com.sh.data.engine.domain.api.model.domain.DataApiCallRecordDomain;
import com.sh.data.engine.domain.api.service.DataApiCallRecordService;
import com.sh.data.engine.domain.api.service.DataApiCallStatisticsService;
import com.sh.data.engine.domain.api.service.DataApiService;
import com.sh.data.engine.domain.api.service.DataApiUserStatisticsService;
import com.sh.data.engine.domain.workspace.manager.model.domain.UserDomain;
import com.sh.data.engine.domain.workspace.manager.service.UserService;
import com.sh.data.engine.job.core.handler.annotation.XxlJob;
import com.sh.data.engine.repository.dao.api.entity.DataApiCallRecordEntity;
import com.sh.data.engine.repository.dao.api.entity.DataApiCallStatisticsEntity;
import com.sh.data.engine.repository.dao.api.entity.DataApiEntity;
import com.sh.data.engine.repository.dao.api.entity.DataApiUserStatisticsEntity;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.time.DateFormatUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.ImmutableTriple;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author: zigui.zdf
 * @description:
 * @date: 2020/11/17 16:22
 */
@Component
@Slf4j
public class DataApiStatisticsTask {

    @Autowired
    private DataApiCallRecordService dataApiCallRecordService;

    @Autowired
    private DataApiCallStatisticsService dataApiCallStatisticsService;

    @Autowired
    private DataApiUserStatisticsService dataApiUserStatisticsService;

    @Autowired
    private DataApiService dataApiService;

    @Autowired
    private UserService userService;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    private static final String DATA_API_CALL_KEY = "data_api_call_key_";

    /**
     * 每5分钟统计一次
     */
    @XxlJob("statisticsDataApi")
    public void statisticsDataApiCall() {

        log.info("开始批量更新数据服务记录");
        String tokenPattern = DATA_API_CALL_KEY + "*";
        Set<String> keys = redisTemplate.keys(tokenPattern);
        try {
            List<String> strings = redisTemplate.opsForValue().multiGet(keys);
            if (CollectionUtils.isNotEmpty(strings)) {
                List<DataApiCallRecordDomain> collect =
                    strings.stream()
                        .map(e -> JSONObject.parseObject(e, DataApiCallRecordDomain.class))
                        .collect(Collectors.toList())
                        .stream()
                        .collect(Collectors.toList());
                dataApiCallRecordService.updateRecordBatch(collect);
            }
        } catch (Exception e) {
            log.error("批量更新数据服务记录失败");
        } finally {
            // 不管成功与否，记录删除
            redisTemplate.delete(keys);
        }


        log.info("开始API概览页面数据统计");
        LambdaQueryWrapper<DataApiCallRecordEntity> queryWrapper =
            new LambdaQueryWrapper<DataApiCallRecordEntity>()
                .eq(DataApiCallRecordEntity::getIsProcessed, 0)
                .isNotNull(DataApiCallRecordEntity::getCallStatus)
                .isNotNull(DataApiCallRecordEntity::getDataApiId);

        Long totalCount = dataApiCallRecordService.count(queryWrapper);
        int pageSize = 1000;
        int totalPages = (int) Math.ceil((double) totalCount / pageSize);
        Page<DataApiCallRecordEntity> page = new Page<>(1, pageSize);
        while (page.getCurrent() <= totalPages) {
            List<DataApiCallRecordEntity> dataApiCallRecords =
                dataApiCallRecordService.page(page, queryWrapper).getRecords();
            // 处理当前页的数据
            executeDataApiCalled(dataApiCallRecords);
            // 切换到下一页
            page.setCurrent(page.getCurrent() + 1);
        }
        log.info("API概览页面数据统计结束");
    }

    public void executeDataApiCalled(List<DataApiCallRecordEntity> dataApiCallRecords) {
        Date now = new Date();
        Map<String, List<DataApiCallRecordEntity>> dateEntitiesMap =
            dataApiCallRecords.stream()
                .collect(
                    Collectors.groupingBy(
                        x -> DateFormatUtils.format(x.getCallBeginTime(), "yyyy-MM-dd")));

        dateEntitiesMap.forEach(
            (date, entities) -> {
                Map<ImmutablePair<Long, String>, List<DataApiCallRecordEntity>> immutablePairListMap =
                    entities.stream()
                        .collect(
                            Collectors.groupingBy(
                                x -> new ImmutablePair(x.getProjectId(), x.getUserId())));

                immutablePairListMap.forEach(
                    (pair, list) -> {
                        Long projectId = pair.getLeft();
                        String userId = pair.getRight();

                        ImmutableTriple<Long, Long, Long> count = this.count(list);

                        long total = count.getLeft();
                        long succ = count.getMiddle();
                        long err = count.getRight();
                        LambdaQueryWrapper<DataApiCallStatisticsEntity> eq =
                            new LambdaQueryWrapper<DataApiCallStatisticsEntity>()
                                .eq(DataApiCallStatisticsEntity::getProjectId, projectId)
                                .eq(
                                    DataApiCallStatisticsEntity::getStatisticsDate,
                                    new Date(DateUtil.toLong(date, DATE_PATTERN.yyyyMMdd)));
                        if (Objects.nonNull(userId)) {
                            eq.eq(DataApiCallStatisticsEntity::getUserId, userId);
                        } else {
                            eq.isNull(DataApiCallStatisticsEntity::getUserId);
                        }

                        DataApiCallStatisticsEntity dataApiCallStatisticsEntity =
                            dataApiCallStatisticsService.getOne(eq, false);
                        if (null == dataApiCallStatisticsEntity) {
                            dataApiCallStatisticsEntity = new DataApiCallStatisticsEntity();
                            dataApiCallStatisticsEntity.setProjectId(projectId);
                            dataApiCallStatisticsEntity.setUserId(userId);
                            dataApiCallStatisticsEntity.setTotalCount(total);
                            dataApiCallStatisticsEntity.setSuccCount(succ);
                            dataApiCallStatisticsEntity.setFailCount(err);
                            dataApiCallStatisticsEntity.setStatisticsDate(
                                new Date(DateUtil.toLong(date, DATE_PATTERN.yyyyMMdd)));
                            dataApiCallStatisticsEntity.setCreateTime(now);
                            dataApiCallStatisticsEntity.setUpdateTime(now);
                            dataApiCallStatisticsService.save(dataApiCallStatisticsEntity);
                        } else {
                            dataApiCallStatisticsEntity.setTotalCount(
                                total + dataApiCallStatisticsEntity.getTotalCount());
                            dataApiCallStatisticsEntity.setSuccCount(
                                succ + dataApiCallStatisticsEntity.getSuccCount());
                            dataApiCallStatisticsEntity.setFailCount(
                                err + dataApiCallStatisticsEntity.getFailCount());
                            dataApiCallStatisticsEntity.setStatisticsDate(
                                new Date(DateUtil.toLong(date, DATE_PATTERN.yyyyMMdd)));
                            dataApiCallStatisticsEntity.setUpdateTime(now);
                            dataApiCallStatisticsService.updateById(dataApiCallStatisticsEntity);
                        }
                    });
            });

        // -------------------更新 data_api_call_statistics  开始----------------------

        // -------------------更新 data_api_user_statistics  开始----------------------

        Map<ImmutableTriple<Long, Long, String>, List<DataApiCallRecordEntity>> immutableTripleListMap =
            dataApiCallRecords.stream()
                .collect(
                    Collectors.groupingBy(
                        x -> new ImmutableTriple(x.getProjectId(), x.getDataApiId(), x.getUserId())));

        immutableTripleListMap.forEach(
            (triple, list) -> {
                Long projectId = triple.getLeft();
                Long apiId = triple.getMiddle();
                String userId = triple.getRight();
                String username = "";
                LambdaQueryWrapper<DataApiUserStatisticsEntity> eq =
                    new LambdaQueryWrapper<DataApiUserStatisticsEntity>()
                        .eq(DataApiUserStatisticsEntity::getProjectId, projectId)
                        .eq(DataApiUserStatisticsEntity::getApiId, apiId);

                if (Objects.nonNull(userId)) {
                    eq.eq(DataApiUserStatisticsEntity::getUserId, userId);
                    UserDomain userDomain = userService.getUserById(userId);
                    if (null != userDomain) {
                        username = userDomain.getUserName();
                    }
                    ImmutableTriple<Long, Long, Long> count = this.count(list);
                    long total = count.getLeft();
                    long succ = count.getMiddle();
                    long err = count.getRight();

                    DataApiUserStatisticsEntity dataApiUserStatisticsEntity =
                        dataApiUserStatisticsService.getOne(eq, false);
                    if (null == dataApiUserStatisticsEntity && total > 0) {
                        dataApiUserStatisticsEntity = new DataApiUserStatisticsEntity();
                        dataApiUserStatisticsEntity.setApiId(Long.valueOf(apiId));
                        if (Objects.nonNull(userId)) {
                            dataApiUserStatisticsEntity.setUserId(userId);
                            dataApiUserStatisticsEntity.setUsername(username);
                        }
                        dataApiUserStatisticsEntity.setProjectId(Long.valueOf(projectId));
                        dataApiUserStatisticsEntity.setTotalCount(total);
                        dataApiUserStatisticsEntity.setSuccCount(succ);
                        dataApiUserStatisticsEntity.setFailCount(err);
                        dataApiUserStatisticsEntity.setCreateTime(now);
                        dataApiUserStatisticsEntity.setUpdateTime(now);
                        dataApiUserStatisticsService.save(dataApiUserStatisticsEntity);
                    } else {
                        dataApiUserStatisticsEntity.setTotalCount(
                            total + dataApiUserStatisticsEntity.getTotalCount());
                        dataApiUserStatisticsEntity.setSuccCount(
                            succ + dataApiUserStatisticsEntity.getSuccCount());
                        dataApiUserStatisticsEntity.setFailCount(
                            err + dataApiUserStatisticsEntity.getFailCount());
                        dataApiUserStatisticsEntity.setUpdateTime(now);
                        dataApiUserStatisticsService.updateById(dataApiUserStatisticsEntity);
                    }
                }
            });

        // -------------------更新 data_api_user_statistics  开始----------------------

        // -------------------更新 data_api  开始----------------------
        Map<Long, List<DataApiCallRecordEntity>> listMap =
            dataApiCallRecords.stream()
                .collect(Collectors.groupingBy(DataApiCallRecordEntity::getDataApiId));
        listMap.forEach(
            (apiId, records) -> {
                DataApiEntity entity = dataApiService.getById(apiId);
                if (null != entity) {
                    Long succCount = entity.getSuccessCount() == null ? 0L : entity.getSuccessCount();
                    Long failCount = entity.getFailCount() == null ? 0L : entity.getFailCount();

                    long count =
                        records.stream()
                            .filter(
                                r ->
                                    Objects.equals(
                                        r.getCallStatus(), DataApiCallStatusEnum.SUCCESS.getStatus()))
                            .count();

                    DataApiEntity api = new DataApiEntity();
                    api.setId(apiId);
                    api.setSuccessCount(succCount + count);
                    api.setFailCount(failCount + (records.size() - count));
                    api.setTotalCount(api.getSuccessCount() + api.getFailCount());
                    dataApiService.updateById(api);
                }
            });

        // -------------------更新 data_api  结束----------------------

        for (DataApiCallRecordEntity dataApiCallRecord : dataApiCallRecords) {
            dataApiCallRecord.setIsProcessed(1);
            dataApiCallRecordService.updateById(dataApiCallRecord);
        }
    }

    private ImmutableTriple<Long, Long, Long> count(List<DataApiCallRecordEntity> list) {
        long total = 0;
        long succ = 0;
        long err = 0;
        for (DataApiCallRecordEntity entity : list) {
            total++;
            Integer callStatus = entity.getCallStatus();
            if (Objects.equals(callStatus, DataApiCallStatusEnum.SUCCESS.getStatus())) {
                succ++;
            } else {
                err++;
            }
        }

        ImmutableTriple<Long, Long, Long> triple = new ImmutableTriple<>(total, succ, err);
        return triple;
    }
}
